/*
 * drivers/net/phy/motorcomm.c
 *
 * Driver for Motorcomm PHYs
 *
 * Author: Leilei Zhao <leilei.zhao@motorcomm.com>
 *
 * Copyright (c) 2019 Motorcomm, Inc.
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 *
 * Support : Motorcomm Phys:
 *		Giga phys: yt8511, yt8521
 *		100/10 Phys : yt8512, yt8512b, yt8510
 *		Automotive 100Mb Phys : yt8010
 *		Automotive 100/10 hyper range Phys: yt8510
 */

#include <config.h>
#include <common.h>
#include <phy.h>

#include "motorcomm_phy.h"

#define BIT(nr)			(1UL << (nr))

static int ytphy_read_ext(struct phy_device *phydev, u32 regnum)
{
	int ret;
	int val;

	ret = phy_write(phydev, MDIO_DEVAD_NONE, REG_DEBUG_ADDR_OFFSET, regnum);
	if (ret < 0)
		return ret;

	val = phy_read(phydev, MDIO_DEVAD_NONE, REG_DEBUG_DATA);

	return val;
}

static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val)
{
	int ret;

	ret = phy_write(phydev, MDIO_DEVAD_NONE, REG_DEBUG_ADDR_OFFSET, regnum);
	if (ret < 0)
		return ret;

	ret = phy_write(phydev, MDIO_DEVAD_NONE, REG_DEBUG_DATA, val);

	return ret;
}

static int yt8512_clk_init(struct phy_device *phydev)
{
	int ret;
	int val;

	val = ytphy_read_ext(phydev, YT8512_EXTREG_AFE_PLL);
	if (val < 0)
		return val;

	val |= YT8512_CONFIG_PLL_REFCLK_SEL_EN;

	ret = ytphy_write_ext(phydev, YT8512_EXTREG_AFE_PLL, val);
	if (ret < 0)
		return ret;

	val = ytphy_read_ext(phydev, YT8512_EXTREG_EXTEND_COMBO);
	if (val < 0)
		return val;

	val |= YT8512_CONTROL1_RMII_EN;

	ret = ytphy_write_ext(phydev, YT8512_EXTREG_EXTEND_COMBO, val);
	if (ret < 0)
		return ret;

	val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
	if (val < 0)
		return val;

	val |= YT_SOFTWARE_RESET;
	ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, val);

	return ret;
}

static int yt8512_led_init(struct phy_device *phydev)
{
	int ret;
	int val;
	int mask;

	val = ytphy_read_ext(phydev, YT8512_EXTREG_LED0);
	if (val < 0)
		return val;

	val |= YT8512_LED0_ACT_BLK_IND;

	mask = YT8512_LED0_DIS_LED_AN_TRY | YT8512_LED0_BT_BLK_EN |
		YT8512_LED0_HT_BLK_EN | YT8512_LED0_COL_BLK_EN |
		YT8512_LED0_BT_ON_EN;
	val &= ~mask;

	ret = ytphy_write_ext(phydev, YT8512_EXTREG_LED0, val);
	if (ret < 0)
		return ret;

	val = ytphy_read_ext(phydev, YT8512_EXTREG_LED1);
	if (val < 0)
		return val;

	val |= YT8512_LED1_BT_ON_EN;

	mask = YT8512_LED1_TXACT_BLK_EN | YT8512_LED1_RXACT_BLK_EN;
	val &= ~mask;

	ret = ytphy_write_ext(phydev, YT8512_LED1_BT_ON_EN, val);

	return ret;
}

static int yt8511_config(struct phy_device *phydev)
{
	int ret;
	int val;

	ret = genphy_config(phydev);
	if (ret < 0)
		return ret;

	/* disable auto sleep */
	val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1);
	if (val < 0)
		return val;

	val &= (~BIT(YT8521_EN_SLEEP_SW_BIT));

	ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val);
	if (ret < 0)
		return ret;

	/* enable RXC clock when no wire plug */
	ret = ytphy_write_ext(phydev, 0xa000, 0);
	if (ret < 0)
		return ret;

	val = ytphy_read_ext(phydev, 0xa);
	val &= ~(1 << 10);
	ytphy_write_ext(phydev, 0xa, val);

	val = ytphy_read_ext(phydev, 0xc);
	if (val < 0)
		return val;

	val &= ~(1 << 12);

	/*phy outpu 125MHz*/
	val |= (1 << 1);
	val |= (1 << 2);

	/*tx delay 850ps tx delay= N*150-350 ps*/
//	val |= (1 << 4);
//	val |= (1 << 5);
//	val |= (1 << 6);
	val |= (1 << 7);

	ret = ytphy_write_ext(phydev, 0xc, val);
	if (ret < 0)
		return ret;

	return ret;
}

static int yt8512_config(struct phy_device *phydev)
{
	int ret;
	int val;

	ret = genphy_config(phydev);
	if (ret < 0)
		return ret;

	ret = yt8512_clk_init(phydev);
	if (ret < 0)
		return ret;

	ret = yt8512_led_init(phydev);

	/* disable auto sleep */
	val = ytphy_read_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1);
	if (val < 0)
		return val;

	val &= (~BIT(YT8512_EN_SLEEP_SW_BIT));

	ret = ytphy_write_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1, val);
	if (ret < 0)
		return ret;

	return ret;
}

static int yt8521_config(struct phy_device *phydev)
{
	int ret;
	int val;

	ret = genphy_config(phydev);
	if (ret < 0)
		return ret;

	/* disable auto sleep */
	val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1);
	if (val < 0)
		return val;

	val &= (~BIT(YT8521_EN_SLEEP_SW_BIT));

	ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val);
	if (ret < 0)
		return ret;

	/* enable RXC clock when no wire plug */
	ret = ytphy_write_ext(phydev, 0xa000, 0);
	if (ret < 0)
		return ret;

	val = ytphy_read_ext(phydev, 0xc);
	if (val < 0)
		return val;
	val &= ~(1 << 12);
	ret = ytphy_write_ext(phydev, 0xc, val);
	if (ret < 0)
		return ret;

	return ret;
}

static struct phy_driver YT8010_driver = {
	.name = "YT8010 Automotive Ethernet",
	.uid = PHY_ID_YT8010,
	.mask = MOTORCOMM_PHY_ID_MASK,
	.features = PHY_BASIC_FEATURES,
	.config = &genphy_config,
	.startup = &genphy_startup,
	.shutdown = &genphy_shutdown,
};

static struct phy_driver YT8510_driver = {
	.name = "YT8510 100/10Mb Ethernet",
	.uid = PHY_ID_YT8510,
	.mask = MOTORCOMM_PHY_ID_MASK,
	.features = PHY_BASIC_FEATURES,
	.config = &genphy_config,
	.startup = &genphy_startup,
	.shutdown = &genphy_shutdown,
};

/* Support for YT8511 PHY */
static struct phy_driver YT8511_driver = {
	.name = "YT8511 Gigabit Ethernet",
	.uid = PHY_ID_YT8511,
	.mask = MOTORCOMM_PHY_ID_MASK,
	.features = PHY_BASIC_FEATURES | PHY_GBIT_FEATURES,
	.config = &yt8511_config,
	.startup = &genphy_startup,
	.shutdown = &genphy_shutdown,
};

static struct phy_driver YT8512_driver = {
	.name = "YT8512 Ethernet",
	.uid = PHY_ID_YT8512,
	.mask = MOTORCOMM_PHY_ID_MASK,
	.features = PHY_BASIC_FEATURES,
	.config = &yt8512_config,
	.startup = &genphy_startup,
	.shutdown = &genphy_shutdown,
};

static struct phy_driver YT8512B_driver = {
	.name = "YT8512B Ethernet",
	.uid = PHY_ID_YT8512B,
	.mask = MOTORCOMM_PHY_ID_MASK,
	.features = PHY_BASIC_FEATURES,
	.config = &yt8512_config,
	.startup = &genphy_startup,
	.shutdown = &genphy_shutdown,
};

static struct phy_driver YT8521_driver = {
	.name = "YT8521 Ethernet",
	.uid = PHY_ID_YT8521,
	.mask = MOTORCOMM_PHY_ID_MASK,
	.features = PHY_BASIC_FEATURES | PHY_GBIT_FEATURES,
	.config = &yt8521_config,
	.startup = &genphy_startup,
	.shutdown = &genphy_shutdown,
};

int phy_motorcomm_init(void)
{
	phy_register(&YT8010_driver);
	phy_register(&YT8510_driver);
	phy_register(&YT8511_driver);
	phy_register(&YT8512_driver);
	phy_register(&YT8512B_driver);
	phy_register(&YT8521_driver);

	return 0;
}
